Old Crypto Server [Crypto]

Old Crypto Server

We found a very old crypto server. He has a secret that we want to get. However, he will not give it up so easily. Flag form for this task is "Aero{[0-9a-f]{32}}"

  • nc tasks.aeroctf.com 44323
  • File: -

Recon

We're given the source code to the python program that's listening on the challenge service. On connection, the program gives us a simple menu with some options:

1. Encrypt
2. Decrypt
3. Get server secret
4. Exit
> 

Looking at the source code, options 1 and 2 don't seem useful. However, with option 3, we're asked to give a "salt" and then we get $$AESEncrypt_{ECB}(S_k, Pad(Salt|Flag))$$.

Since this is ECB mode, we know that we can leak the encrypted flag character by character since we control a prefix, which is the salt in this case. We do so by asking the server to give us the server secret with a padding that we control such that the next character we're trying to bruteforce is the last character in the block that contains our prefix.

We used a modified version of brute_ecb_suffix from p4's crypto commons which takes into account the fact that the suffix is not a multiple of the AES block size and uses a different alphabet to speed things up.

Solution

pip install crypto-commons

from pwn import *
from crypto_commons.generic import chunk

conn = remote("tasks.aeroctf.com", 44323)
print(conn.recvuntil("> "))

ALPHABET = [ord(x) for x in "1234567890abcdefAero{}"]


def encrypt(msg):
    conn.send_raw("3\n")
    conn.recvuntil('salt: ')
    conn.send_raw(msg + '\n')
    conn.recvuntil(": b'")
    encrypted = conn.recvuntil("'")[:-1]
    conn.recvuntil("> ")
    return b64d(encrypted)


def brute_ecb_suffix(encrypt_function, block_size=16,
                     expected_suffix_len=32, pad_char='A',
                     alphabet=list(range(256))):
    suffix = ""
    padding_len = block_size - (expected_suffix_len % block_size)
    padded_suffix_len = expected_suffix_len + padding_len
    recovery_block = padded_suffix_len / block_size - 1
    alphabet_chars = [chr(x) for x in alphabet]

    for i in range(padded_suffix_len - len(suffix) - 1, padding_len-1, -1):
        data = pad_char * i
        correct = chunk(encrypt_function(data),
                        block_size)[recovery_block]

        for char in alphabet_chars:
            test = data + suffix + char
            try:
                encrypted = chunk(encrypt_function(test),
                                  block_size)[recovery_block]

                if correct == encrypted:
                    suffix += char
                    print('FOUND', padded_suffix_len - i, char)
                    break

            except Exception as ex:
                pass

    return suffix


print(brute_ecb_suffix(encrypt,
                       block_size=16,
                       expected_suffix_len=38,
                       pad_char='A',
                       alphabet=ALPHABET))

Flag

[+] Opening connection to tasks.aeroctf.com on port 44323: Done
1. Encrypt
2. Decrypt
3. Get server secret
4. Exit
> 
('FOUND', 1, 'A')
('FOUND', 2, 'e')
('FOUND', 3, 'r')
('FOUND', 4, 'o')
...
Aero{5013a76ed3b98bae1e79169b3495f47a}

Aero{5013a76ed3b98bae1e79169b3495f47a}